package com.mask.token.util;

import com.mask.token.config.MaskTokenProperties;
import com.mask.token.service.MaskTokenKeyService;
import com.mask.token.service.MaskTokenValidationService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT Token诊断工具
 * 用于诊断和修复JWT签名问题
 *
 * @author mask
 * @since 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(name = "mask.token.diagnostic.enabled", havingValue = "true", matchIfMissing = false)
public class MaskTokenDiagnostic {

    private final MaskTokenProperties properties;
    private final MaskTokenKeyService keyService;
    private final MaskTokenValidationService validationService;

    /**
     * 诊断JWT签名问题
     */
    public DiagnosticResult diagnoseJwtSignature(String token) {
        DiagnosticResult result = new DiagnosticResult();
        result.setToken(token);
        result.setTimestamp(Instant.now());

        try {
            // 1. 检查Token格式
            if (!isValidTokenFormat(token)) {
                result.setValid(false);
                result.setError("Token格式无效");
                result.addSuggestion("请检查Token是否完整，应该包含三个部分用点分隔");
                return result;
            }

            // 2. 检查密钥配置
            if (!validateKeyConfiguration()) {
                result.setValid(false);
                result.setError("密钥配置无效");
                result.addSuggestion("请检查JWT密钥配置，确保密钥长度至少32字节");
                return result;
            }

            // 3. 尝试解析Token
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(keyService.getSigningKey())
                        .parseClaimsJws(token)
                        .getBody();

                result.setValid(true);
                result.setSubject(claims.getSubject());
                result.setExpiration(claims.getExpiration());
                result.addSuggestion("Token验证成功，签名正确");

            } catch (Exception e) {
                result.setValid(false);
                result.setError("Token解析失败: " + e.getMessage());
                
                // 根据错误类型提供具体建议
                if (e.getMessage().contains("signature")) {
                    result.addSuggestion("签名验证失败，可能原因：");
                    result.addSuggestion("1. 密钥不匹配 - 检查生成和验证时使用的密钥是否相同");
                    result.addSuggestion("2. 密钥格式问题 - 确保密钥编码正确");
                    result.addSuggestion("3. 算法不匹配 - 确保使用相同的签名算法");
                } else if (e.getMessage().contains("expired")) {
                    result.addSuggestion("Token已过期，请重新生成Token");
                } else if (e.getMessage().contains("malformed")) {
                    result.addSuggestion("Token格式错误，请检查Token是否被截断或损坏");
                }
            }

        } catch (Exception e) {
            result.setValid(false);
            result.setError("诊断过程中发生错误: " + e.getMessage());
            result.addSuggestion("请检查系统配置和依赖");
        }

        return result;
    }

    /**
     * 测试密钥生成和验证
     */
    public KeyTestResult testKeyGeneration() {
        KeyTestResult result = new KeyTestResult();
        result.setTimestamp(Instant.now());

        try {
            // 1. 测试密钥生成
            SecretKey key = keyService.getSigningKey();
            result.setKeyGenerated(true);
            result.setKeyLength(key.getEncoded().length);

            // 2. 测试Token生成
            String testToken = generateTestToken(key);
            result.setTokenGenerated(true);
            result.setTestToken(testToken);

            // 3. 测试Token验证
            boolean isValid = validationService.quickValidateToken(testToken);
            result.setTokenValid(isValid);

            if (isValid) {
                result.setSuccess(true);
                result.setMessage("密钥生成和验证测试成功");
            } else {
                result.setSuccess(false);
                result.setMessage("Token验证失败");
            }

        } catch (Exception e) {
            result.setSuccess(false);
            result.setMessage("测试失败: " + e.getMessage());
            result.setError(e.getMessage());
        }

        return result;
    }

    /**
     * 生成测试Token
     */
    private String generateTestToken(SecretKey key) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", 1L);
        claims.put("username", "test-user");
        claims.put("test", true);

        return Jwts.builder()
                .setClaims(claims)
                .setSubject("test-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时后过期
                .signWith(SignatureAlgorithm.HS512, key)
                .compact();
    }

    /**
     * 检查Token格式
     */
    private boolean isValidTokenFormat(String token) {
        if (token == null || token.trim().isEmpty()) {
            return false;
        }

        String[] parts = token.split("\\.");
        return parts.length == 3;
    }

    /**
     * 验证密钥配置
     */
    private boolean validateKeyConfiguration() {
        try {
            String secret = properties.getSecret();
            if (secret == null || secret.trim().isEmpty()) {
                return false;
            }

            byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
            return keyBytes.length >= 32;

        } catch (Exception e) {
            log.error("验证密钥配置失败", e);
            return false;
        }
    }

    /**
     * 获取系统信息
     */
    public SystemInfo getSystemInfo() {
        SystemInfo info = new SystemInfo();
        info.setTimestamp(Instant.now());
        info.setJavaVersion(System.getProperty("java.version"));
        info.setOsName(System.getProperty("os.name"));
        info.setOsVersion(System.getProperty("os.version"));
        info.setJwtSecretLength(properties.getSecret().length());
        info.setJwtSecretPreview(properties.getSecret().substring(0, Math.min(8, properties.getSecret().length())) + "...");
        
        return info;
    }

    /**
     * 诊断结果
     */
    public static class DiagnosticResult {
        private String token;
        private boolean valid;
        private String error;
        private String subject;
        private Date expiration;
        private java.util.List<String> suggestions = new java.util.ArrayList<>();
        private Instant timestamp;

        // Getters and Setters
        public String getToken() { return token; }
        public void setToken(String token) { this.token = token; }
        public boolean isValid() { return valid; }
        public void setValid(boolean valid) { this.valid = valid; }
        public String getError() { return error; }
        public void setError(String error) { this.error = error; }
        public String getSubject() { return subject; }
        public void setSubject(String subject) { this.subject = subject; }
        public Date getExpiration() { return expiration; }
        public void setExpiration(Date expiration) { this.expiration = expiration; }
        public java.util.List<String> getSuggestions() { return suggestions; }
        public void setSuggestions(java.util.List<String> suggestions) { this.suggestions = suggestions; }
        public Instant getTimestamp() { return timestamp; }
        public void setTimestamp(Instant timestamp) { this.timestamp = timestamp; }

        public void addSuggestion(String suggestion) {
            this.suggestions.add(suggestion);
        }
    }

    /**
     * 密钥测试结果
     */
    public static class KeyTestResult {
        private boolean success;
        private String message;
        private String error;
        private boolean keyGenerated;
        private int keyLength;
        private boolean tokenGenerated;
        private String testToken;
        private boolean tokenValid;
        private Instant timestamp;

        // Getters and Setters
        public boolean isSuccess() { return success; }
        public void setSuccess(boolean success) { this.success = success; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
        public String getError() { return error; }
        public void setError(String error) { this.error = error; }
        public boolean isKeyGenerated() { return keyGenerated; }
        public void setKeyGenerated(boolean keyGenerated) { this.keyGenerated = keyGenerated; }
        public int getKeyLength() { return keyLength; }
        public void setKeyLength(int keyLength) { this.keyLength = keyLength; }
        public boolean isTokenGenerated() { return tokenGenerated; }
        public void setTokenGenerated(boolean tokenGenerated) { this.tokenGenerated = tokenGenerated; }
        public String getTestToken() { return testToken; }
        public void setTestToken(String testToken) { this.testToken = testToken; }
        public boolean isTokenValid() { return tokenValid; }
        public void setTokenValid(boolean tokenValid) { this.tokenValid = tokenValid; }
        public Instant getTimestamp() { return timestamp; }
        public void setTimestamp(Instant timestamp) { this.timestamp = timestamp; }
    }

    /**
     * 系统信息
     */
    public static class SystemInfo {
        private Instant timestamp;
        private String javaVersion;
        private String osName;
        private String osVersion;
        private int jwtSecretLength;
        private String jwtSecretPreview;

        // Getters and Setters
        public Instant getTimestamp() { return timestamp; }
        public void setTimestamp(Instant timestamp) { this.timestamp = timestamp; }
        public String getJavaVersion() { return javaVersion; }
        public void setJavaVersion(String javaVersion) { this.javaVersion = javaVersion; }
        public String getOsName() { return osName; }
        public void setOsName(String osName) { this.osName = osName; }
        public String getOsVersion() { return osVersion; }
        public void setOsVersion(String osVersion) { this.osVersion = osVersion; }
        public int getJwtSecretLength() { return jwtSecretLength; }
        public void setJwtSecretLength(int jwtSecretLength) { this.jwtSecretLength = jwtSecretLength; }
        public String getJwtSecretPreview() { return jwtSecretPreview; }
        public void setJwtSecretPreview(String jwtSecretPreview) { this.jwtSecretPreview = jwtSecretPreview; }
    }
}
